home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 15 / CU Amiga Magazine's Super CD-ROM 15 (1997)(EMAP Images)(GB)[!][issue 1997-10].iso / CUCD / Graphics / Ghostscript / source / gxhint2.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-03-30  |  11.6 KB  |  396 lines

  1. /* Copyright (C) 1990, 1995, 1997 Aladdin Enterprises.  All rights reserved.
  2.   
  3.   This file is part of Aladdin Ghostscript.
  4.   
  5.   Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author
  6.   or distributor accepts any responsibility for the consequences of using it,
  7.   or for whether it serves any particular purpose or works at all, unless he
  8.   or she says so in writing.  Refer to the Aladdin Ghostscript Free Public
  9.   License (the "License") for full details.
  10.   
  11.   Every copy of Aladdin Ghostscript must include a copy of the License,
  12.   normally in a plain ASCII text file named PUBLIC.  The License grants you
  13.   the right to copy, modify and redistribute Aladdin Ghostscript, but only
  14.   under certain conditions described in the License.  Among other things, the
  15.   License requires that the copyright notice and this notice be preserved on
  16.   all copies.
  17. */
  18.  
  19. /* gxhint2.c */
  20. /* Character level hints for Type 1 fonts. */
  21. #include "memory_.h"
  22. #include "gx.h"
  23. #include "gserrors.h"
  24. #include "gxarith.h"
  25. #include "gxfixed.h"
  26. #include "gxmatrix.h"
  27. #include "gxchar.h"
  28. #include "gxfont.h"
  29. #include "gxfont1.h"
  30. #include "gxtype1.h"
  31.  
  32. /* Define the tolerance for testing whether a point is in a zone, */
  33. /* in device pixels.  (Maybe this should be variable?) */
  34. #define stem_tolerance float2fixed(0.05)
  35.  
  36. /* Forward references */
  37.  
  38. private stem_hint *near type1_stem(P3(stem_hint_table *, fixed, fixed));
  39. private fixed near find_snap(P3(fixed, const stem_snap_table *, const pixel_scale *));
  40. private alignment_zone *near find_zone(P3(gs_type1_state *, fixed, fixed));
  41.  
  42. /* Reset the stem hints. */
  43. void
  44. reset_stem_hints(gs_type1_state *pcis)
  45. {    pcis->hstem_hints.count = pcis->hstem_hints.replaced_count = 0;
  46.     pcis->vstem_hints.count = pcis->vstem_hints.replaced_count = 0;
  47.     update_stem_hints(pcis);
  48. }
  49.  
  50. /* Prepare to replace the stem hints. */
  51. private void
  52. save_replaced_hints(stem_hint_table *psht)
  53. {    int rep_count = min(psht->replaced_count + psht->count, max_stems);
  54.  
  55.     memmove(&psht->data[max_stems - rep_count], &psht->data[0],
  56.         psht->count * sizeof(psht->data[0]));
  57.     psht->replaced_count = rep_count;
  58.     psht->count = psht->current = 0;
  59. }
  60. void
  61. type1_replace_stem_hints(gs_type1_state *pcis)
  62. {    if_debug2('y', "[y]saving hints: %d hstem, %d vstem\n",
  63.           pcis->hstem_hints.count, pcis->vstem_hints.count);
  64.     save_replaced_hints(&pcis->hstem_hints);
  65.     save_replaced_hints(&pcis->vstem_hints);
  66.     if_debug2('y', "[y]total saved hints: %d hstem, %d vstem\n",
  67.           pcis->hstem_hints.replaced_count,
  68.           pcis->vstem_hints.replaced_count);
  69. }
  70.  
  71. /* Update the internal stem hint pointers after moving or copying the state. */
  72. void
  73. update_stem_hints(gs_type1_state *pcis)
  74. {    pcis->hstem_hints.current = 0;
  75.     pcis->vstem_hints.current = 0;
  76. }
  77.  
  78. /* ------ Add hints ------ */
  79.  
  80. #undef c_fixed
  81. #define c_fixed(d, c) m_fixed(d, c, pcis->fc, max_coeff_bits)
  82.  
  83. #ifdef DEBUG
  84. private void
  85. print_add_stem(const char *msg, const stem_hint_table *psht,
  86.   const stem_hint *psh, fixed c, fixed dc, fixed v, fixed dv)
  87. {    if_debug10('y', "[y]%s %d/%d: %g,%g -> %g(%g)%g ; d = %g,%g\n",
  88.            msg, (int)(psh - &psht->data[0]), psht->count,
  89.            fixed2float(c), fixed2float(dc),
  90.            fixed2float(v), fixed2float(dv), fixed2float(v + dv),
  91.            fixed2float(psh->dv0), fixed2float(psh->dv1));
  92. }
  93. #else
  94. #  define print_add_stem(msg, psht, psh, c, dc, v, dv) DO_NOTHING
  95. #endif
  96.  
  97. /* Compute and store the adjusted stem coordinates. */
  98. private void
  99. store_stem_deltas(const stem_hint_table *psht, stem_hint *psh,
  100.   const pixel_scale *psp, fixed v, fixed dv, fixed adj_dv)
  101. {    /*
  102.      * We want to align the stem so its edges fall on pixel boundaries
  103.      * (possibly "big pixel" boundaries if we are oversampling),
  104.      * but if hint replacement has occurred, we must shift edges in a
  105.      * consistent way.  This is a real nuisance, but I don't see how
  106.      * to avoid it; if we don't do it, we get bizarre anomalies like
  107.      * disappearing stems.
  108.      */
  109.     const stem_hint *psh0 = 0;
  110.     const stem_hint *psh1 = 0;
  111.     int i;
  112.  
  113.     /*
  114.      * If we ever had a hint with the same edge(s), align this one
  115.      * the same way.
  116.      */
  117.     for ( i = max_stems - psht->replaced_count; i < max_stems; ++i )
  118.       { const stem_hint *ph = &psht->data[i];
  119.         if ( ph == psh )
  120.           continue;
  121.         if ( ph->v0 == psh->v0 )
  122.           psh0 = ph;
  123.         if ( ph->v1 == psh->v1 )
  124.           psh1 = ph;
  125.       }
  126.     for ( i = 0; i < psht->count; ++i )
  127.       { const stem_hint *ph = &psht->data[i];
  128.         if ( ph == psh )
  129.           continue;
  130.         if ( ph->v0 == psh->v0 )
  131.           psh0 = ph;
  132.         if ( ph->v1 == psh->v1 )
  133.           psh1 = ph;
  134.       }
  135.     if ( psh0 != 0 )
  136.       { psh->dv0 = psh0->dv0;
  137.         if ( psh1 != 0 )
  138.           { /* Both edges are determined. */
  139.         psh->dv1 = psh1->dv1;
  140.           }
  141.         else
  142.           { /* Only the lower edge is determined. */
  143.         psh->dv1 = psh->dv0 + adj_dv - dv;
  144.           }
  145.       }
  146.     else if ( psh1 != 0 )
  147.       { /* Only the upper edge is determined. */
  148.         psh->dv1 = psh1->dv1;
  149.         psh->dv0 = psh->dv1 + adj_dv - dv;
  150.       }
  151.     else
  152.       { /* Neither edge is determined. */
  153.         fixed diff2_dv = arith_rshift_1(adj_dv - dv);
  154.         fixed edge = v - diff2_dv;
  155.         fixed diff_v = scaled_rounded(edge, psp) - edge;
  156.  
  157.         psh->dv0 = diff_v - diff2_dv;
  158.         psh->dv1 = diff_v + diff2_dv;
  159.       }
  160. }
  161.  
  162. /* Add a horizontal stem hint. */
  163. void
  164. type1_do_hstem(gs_type1_state *pcis, fixed y, fixed dy,
  165.   const gs_matrix_fixed *pmat)
  166. {    stem_hint *psh;
  167.     alignment_zone *pz;
  168.     const pixel_scale *psp;
  169.     fixed v, dv, adj_dv;
  170.     fixed vtop, vbot;
  171.  
  172.     if ( !pcis->fh.use_y_hints || !pmat->txy_fixed_valid )
  173.       return;
  174.     y += pcis->lsb.y + pcis->adxy.y;
  175.     if ( pcis->fh.axes_swapped )
  176.       { psp = &pcis->scale.x;
  177.         v = pcis->vs_offset.x + c_fixed(y, yx) + pmat->tx_fixed;
  178.         dv = c_fixed(dy, yx);
  179.       }
  180.     else
  181.       { psp = &pcis->scale.y;
  182.         v = pcis->vs_offset.y + c_fixed(y, yy) + pmat->ty_fixed;
  183.         dv = c_fixed(dy, yy);
  184.       }
  185.     if ( dy < 0 )
  186.       vbot = v + dv, vtop = v;
  187.     else
  188.       vbot = v, vtop = v + dv;
  189.     if ( dv < 0 )
  190.       v += dv, dv = -dv;
  191.     psh = type1_stem(&pcis->hstem_hints, v, dv);
  192.     if ( psh == 0 )
  193.       return;
  194.     adj_dv = find_snap(dv, &pcis->fh.snap_h, psp);
  195.     pz = find_zone(pcis, vbot, vtop);
  196.     if ( pz != 0 )
  197.       {    /* Use the alignment zone to align the outer stem edge. */
  198.         int inverted =
  199.           (pcis->fh.axes_swapped ? pcis->fh.x_inverted : pcis->fh.y_inverted);
  200.         int adjust_v1 =
  201.           (inverted ? !pz->is_top_zone : pz->is_top_zone);
  202.         fixed flat_v = pz->flat;
  203.         fixed overshoot =
  204.             (pz->is_top_zone ? vtop - flat_v : flat_v - vbot);
  205.         fixed pos_over =
  206.             (inverted ? -overshoot : overshoot);
  207.         fixed ddv = adj_dv - dv;
  208.         fixed shift = scaled_rounded(flat_v, psp) - flat_v;
  209.  
  210.         if ( pos_over > 0 )
  211.         {
  212.           if ( pos_over < pcis->fh.blue_shift || pcis->fh.suppress_overshoot )
  213.             {    /* Character is small, suppress overshoot. */
  214.             if_debug0('y', "[y]suppress overshoot\n");
  215.             if ( pz->is_top_zone )
  216.               shift -= overshoot;
  217.             else
  218.               shift += overshoot;
  219.             }
  220.           else if ( pos_over < psp->unit )
  221.             {    /* Enforce overshoot. */
  222.             if_debug0('y', "[y]enforce overshoot\n");
  223.             if ( overshoot < 0 )
  224.               overshoot = -psp->unit - overshoot;
  225.             else
  226.               overshoot = psp->unit - overshoot;
  227.             if ( pz->is_top_zone )
  228.               shift += overshoot;
  229.             else
  230.               shift -= overshoot;
  231.             }
  232.         }
  233.         if ( adjust_v1 )
  234.           psh->dv1 = shift, psh->dv0 = shift - ddv;
  235.         else
  236.           psh->dv0 = shift, psh->dv1 = shift + ddv;
  237.         if_debug2('y', "[y]flat_v = %g, overshoot = %g for:\n",
  238.               fixed2float(flat_v), fixed2float(overshoot));
  239.       }
  240.     else
  241.       {    /* Align the stem so its edges fall on pixel boundaries. */
  242.         store_stem_deltas(&pcis->hstem_hints, psh, psp, v, dv, adj_dv);
  243.       }
  244.     print_add_stem("hstem", &pcis->hstem_hints, psh, y, dy, v, dv);
  245. }
  246.  
  247. /* Add a vertical stem hint. */
  248. void
  249. type1_do_vstem(gs_type1_state *pcis, fixed x, fixed dx,
  250.   const gs_matrix_fixed *pmat)
  251. {    stem_hint *psh;
  252.     const pixel_scale *psp;
  253.     fixed v, dv, adj_dv;
  254.  
  255.     if ( !pcis->fh.use_x_hints )
  256.       return;
  257.     x += pcis->lsb.x + pcis->adxy.x;
  258.     if ( pcis->fh.axes_swapped )
  259.       { psp = &pcis->scale.y;
  260.         v = pcis->vs_offset.y + c_fixed(x, xy) + pmat->ty_fixed;
  261.         dv = c_fixed(dx, xy);
  262.       }
  263.     else
  264.       { psp = &pcis->scale.x;
  265.         v = pcis->vs_offset.x + c_fixed(x, xx) + pmat->tx_fixed;
  266.         dv = c_fixed(dx, xx);
  267.       }
  268.     if ( dv < 0 )
  269.       v += dv, dv = -dv;
  270.     psh = type1_stem(&pcis->vstem_hints, v, dv);
  271.     if ( psh == 0 )
  272.       return;
  273.     adj_dv = find_snap(dv, &pcis->fh.snap_v, psp);
  274.     if ( pcis->pfont->data.ForceBold && adj_dv < psp->unit )
  275.       adj_dv = psp->unit;
  276.     /* Align the stem so its edges fall on pixel boundaries. */
  277.     store_stem_deltas(&pcis->vstem_hints, psh, psp, v, dv, adj_dv);
  278.     print_add_stem("vstem", &pcis->vstem_hints, psh, x, dx, v, dv);
  279. }
  280.  
  281. /* Adjust the character center for a vstem3. */
  282. /****** NEEDS UPDATING FOR SCALE ******/
  283. void
  284. type1_do_center_vstem(gs_type1_state *pcis, fixed x0, fixed dx,
  285.   const gs_matrix_fixed *pmat)
  286. {    fixed x1 = x0 + dx;
  287.     gs_fixed_point pt0, pt1, width;
  288.     fixed center, int_width;
  289.     fixed *psxy;
  290.     if ( gs_point_transform2fixed(pmat, fixed2float(x0), 0.0, &pt0) < 0 ||
  291.          gs_point_transform2fixed(pmat, fixed2float(x1), 0.0, &pt1) < 0
  292.        )
  293.       {    /* Punt. */
  294.         return;
  295.       }
  296.     width.x = pt0.x - pt1.x;
  297.     if ( width.x < 0 )
  298.       width.x = - width.x;
  299.     width.y = pt0.y - pt1.y;
  300.     if ( width.y < 0 )
  301.       width.y = - width.y;
  302.     if ( width.y < float2fixed(0.05) )
  303.     {    /* Vertical on device */
  304.         center = arith_rshift_1(pt0.x + pt1.x);
  305.         int_width = fixed_rounded(width.x);
  306.         psxy = &pcis->vs_offset.x;
  307.     }
  308.     else
  309.     {    /* Horizontal on device */
  310.         center = arith_rshift_1(pt0.y + pt1.y);
  311.         int_width = fixed_rounded(width.y);
  312.         psxy = &pcis->vs_offset.y;
  313.     }
  314.     if ( int_width == fixed_0 || (int_width & fixed_1) == 0 )
  315.     {    /* Odd width, center stem over pixel. */
  316.         *psxy = fixed_floor(center) + fixed_half - center;
  317.     }
  318.     else
  319.     {    /* Even width, center stem between pixels. */
  320.         *psxy = fixed_rounded(center) - center;
  321.     }
  322.     /* We can't fix up the current point here, */
  323.     /* but we can fix up everything else. */
  324.     /****** TO BE COMPLETED ******/
  325. }
  326.  
  327. /* Add a stem hint, keeping the table sorted. */
  328. /* We know that d >= 0. */
  329. /* Return the stem hint pointer, or 0 if the table is full. */
  330. private stem_hint *near
  331. type1_stem(stem_hint_table *psht, fixed v0, fixed d)
  332. {    stem_hint *bot = &psht->data[0];
  333.     stem_hint *top = bot + psht->count;
  334.     if ( psht->count >= max_stems )
  335.       return 0;
  336.     while ( top > bot && v0 < top[-1].v0 )
  337.        {    *top = top[-1];
  338.         top--;
  339.        }
  340.     /* Add a little fuzz for insideness testing. */
  341.     top->v0 = v0 - stem_tolerance;
  342.     top->v1 = v0 + d + stem_tolerance;
  343.     psht->count++;
  344.     return top;
  345. }
  346.  
  347. /* Compute the adjusted width of a stem. */
  348. /* The value returned is always a multiple of scale.unit. */
  349. private fixed near
  350. find_snap(fixed dv, const stem_snap_table *psst, const pixel_scale *pps)
  351. {    fixed best = pps->unit;
  352.     fixed adj_dv;
  353.     int i;
  354.     for ( i = 0; i < psst->count; i++ )
  355.     {    fixed diff = psst->data[i] - dv;
  356.         if ( any_abs(diff) < any_abs(best) )
  357.         {    if_debug3('Y', "[Y]possibly snap %g to [%d]%g\n",
  358.                   fixed2float(dv), i,
  359.                   fixed2float(psst->data[i]));
  360.             best = diff;
  361.         }
  362.     }
  363.     adj_dv = scaled_rounded((any_abs(best) < pps->unit ? dv + best : dv),
  364.                 pps);
  365.     if ( adj_dv == 0 )
  366.       adj_dv = pps->unit;
  367. #ifdef DEBUG
  368.     if ( adj_dv == dv )
  369.       if_debug1('Y', "[Y]no snap %g\n", fixed2float(dv));
  370.     else
  371.       if_debug2('Y', "[Y]snap %g to %g\n",
  372.             fixed2float(dv), fixed2float(adj_dv));
  373. #endif
  374.     return adj_dv;
  375. }
  376.  
  377. /* Find the applicable alignment zone for a stem, if any. */
  378. /* vbot and vtop are the bottom and top of the stem, */
  379. /* but without interchanging if the y axis is inverted. */
  380. private alignment_zone *near
  381. find_zone(gs_type1_state *pcis, fixed vbot, fixed vtop)
  382. {    alignment_zone *pz;
  383.     for ( pz = &pcis->fh.a_zones[pcis->fh.a_zone_count];
  384.           --pz >= &pcis->fh.a_zones[0];
  385.         )
  386.     {    fixed v = (pz->is_top_zone ? vtop : vbot);
  387.         if ( v >= pz->v0 && v <= pz->v1 )
  388.         {    if_debug2('Y', "[Y]stem crosses %s-zone %d\n",
  389.                   (pz->is_top_zone ? "top" : "bottom"),
  390.                   (int)(pz - &pcis->fh.a_zones[0]));
  391.             return pz;
  392.         }
  393.     }
  394.     return 0;
  395. }
  396.